package cn.eric.framework.exception.utils;

import cn.eric.framework.ajax.AjaxResponse;
import cn.eric.framework.auth.domain.User;
import cn.eric.framework.exception.dao.BusinessExceptionLogDao;
import cn.eric.framework.exception.domain.exception.BusinessException;
import cn.eric.framework.exception.domain.log.BusinessExceptionLog;
import com.google.gson.Gson;
import org.apache.logging.log4j.Logger;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 异常统一处理器
 */
public class EExceptionHandler implements HandlerExceptionResolver {
    private static final String UN_BUSINESS_ERROR_TITLE = "[un business error]";
    private static final String NO_USER = "no user";
    private static final String TEMPLATE_EXCEPTION_WITH_ADDITIONAL_INFO = "%s [additional info] %s";

    private Logger logger;
    private Logger loggerBusinessError;
    private Gson gson;
    private String headerKeyRemoteIp;
    private String headerKeyUser;
    private BusinessExceptionLogDao businessExceptionLogDao;

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        AjaxResponse<String> response = new AjaxResponse<>();

        // 获取 ip 用户信息 *如果有*
        String remoteIp = getRemoteIp(httpServletRequest);
        User user = (User) httpServletRequest.getAttribute(headerKeyUser);
        String uid = NO_USER;
        if (user != null) {
            uid = user.getUid();
        }

        if (e.getClass() == BusinessException.class) {
            // 注入默认提示信息
            BusinessException be = (BusinessException) e;
            try {
                be.setInfo(EExceptionMap.getBEById(be.getId()).getInfo());
            } catch (NullPointerException npe) {
                be.setInfo("未知异常");
            }
            // 如果携带附加信息就按照模板注入
            if (be.getAdditionalInfo() != null) {
                be.setInfo(String.format(TEMPLATE_EXCEPTION_WITH_ADDITIONAL_INFO, be.getInfo(), be.getAdditionalInfo()));
            }
            BusinessExceptionLog bel = new BusinessExceptionLog(be, uid, remoteIp, httpServletRequest.getRequestURI());
            // 保存业务异常日志
            businessExceptionLogDao.save(bel);
            // 返回数据到前端
            response = new AjaxResponse<>(be.getId(), be.getInfo());
        } else {
            // 处理非业务逻辑异常
            logger.error(UN_BUSINESS_ERROR_TITLE, e);
        }

        httpServletResponse.setStatus(HttpServletResponse.SC_OK);
        httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);

        try {
            httpServletResponse.getWriter().write(gson.toJson(response));
        } catch (IOException exWrite2Response) {
            logger.error(exWrite2Response);
        }

        return new ModelAndView();
    }

    private String getRemoteIp(HttpServletRequest request) {
        if (headerKeyRemoteIp != null) {
            return request.getHeader(headerKeyRemoteIp);
        } else {
            return request.getRemoteAddr();
        }
    }

    public Logger getLogger() {
        return logger;
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
    }

    public Logger getLoggerBusinessError() {
        return loggerBusinessError;
    }

    public void setLoggerBusinessError(Logger loggerBusinessError) {
        this.loggerBusinessError = loggerBusinessError;
    }

    public Gson getGson() {
        return gson;
    }

    public void setGson(Gson gson) {
        this.gson = gson;
    }

    public String getHeaderKeyRemoteIp() {
        return headerKeyRemoteIp;
    }

    public void setHeaderKeyRemoteIp(String headerKeyRemoteIp) {
        this.headerKeyRemoteIp = headerKeyRemoteIp;
    }

    public String getHeaderKeyUser() {
        return headerKeyUser;
    }

    public void setHeaderKeyUser(String headerKeyUser) {
        this.headerKeyUser = headerKeyUser;
    }

    public BusinessExceptionLogDao getBusinessExceptionLogDao() {
        return businessExceptionLogDao;
    }

    public void setBusinessExceptionLogDao(BusinessExceptionLogDao businessExceptionLogDao) {
        this.businessExceptionLogDao = businessExceptionLogDao;
    }
}
